/**
 * 
 */
package com.yi.model.db.mongo;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
import com.mongodb.MongoOptions;
import com.mongodb.ServerAddress;

/**
 * @author yilo
 * 
 */

public class MongoConnection {
	static final Logger LOG = LoggerFactory.getLogger(MongoConnection.class);

	private DB m_db;
	private final MongoOptions m_options = new MongoOptions();
	private List<ServerAddress> m_servers;
	private String m_dbName;
	private String m_userName;
	private String m_password;
	private static final int DEFAULT_MONGO_PORT = 27017;

	public void setConnectionsPerHost(final int a_connectionsPerHost) {
		m_options.connectionsPerHost = a_connectionsPerHost;
	}

	public void setThreadsAllowedToBlockForConnectionMultiplier(
			final int a_threadsAllowedToBlockForConnectionMultiplier) {
		m_options.threadsAllowedToBlockForConnectionMultiplier = a_threadsAllowedToBlockForConnectionMultiplier;
	}

	public void setMaxWaitTime(final int a_maxWaitTime) {
		m_options.maxWaitTime = a_maxWaitTime;
	}

	public void setConnectTimeout(final int a_connectTimeout) {
		m_options.connectTimeout = a_connectTimeout;
	}

	public void setSocketTimeout(final int a_socketTimeout) {
		m_options.socketTimeout = a_socketTimeout;
	}

	public void setAutoConnectRetry(final boolean a_autoConnectRetry) {
		m_options.autoConnectRetry = a_autoConnectRetry;
	}

	public void setSlaveOk(final boolean a_slaveOk) {
		m_options.slaveOk = a_slaveOk;
	}

	public void setSafe(final boolean a_safe) {
		m_options.safe = a_safe;
	}

	public void setWriteConcern(final int a_w) {
		m_options.w = a_w;
	}

	public void setWriteTimeout(final int a_wtimeout) {
		m_options.wtimeout = a_wtimeout;
	}

	public void setFsync(final boolean a_fsync) {
		m_options.fsync = a_fsync;
	}

	public List<ServerAddress> getServers() {
		return m_servers;
	}

	/**
	 * Parses the list of strings. Expected format is host1:port[,
	 * host2:port]...
	 * 
	 * @param a_serverList
	 *            String listing all servers comma separated
	 * @throws java.net.UnknownHostException
	 *             thrown if the host name is no good
	 */
	public void setServerList(final String a_serverList)
			throws UnknownHostException {
		final StringTokenizer st = new StringTokenizer(a_serverList, ",");
		final List<ServerAddress> addrs = new ArrayList<ServerAddress>();
		while (st.hasMoreTokens()) {
			final String hostAndPort = st.nextToken();
			final int colonIdx = hostAndPort.indexOf(":");
			final int port = colonIdx == -1 ? DEFAULT_MONGO_PORT : Integer
					.parseInt(hostAndPort.substring(colonIdx + 1));
			final String host = colonIdx == -1 ? hostAndPort : hostAndPort
					.substring(0, colonIdx);
			addrs.add(new ServerAddress(host, port));
		}
		setServers(addrs);
	}

	public void setServers(final List<ServerAddress> a_servers) {
		m_servers = a_servers;
	}

	public String getDbName() {
		return m_dbName;
	}

	public void setDbName(final String a_db) {
		m_dbName = a_db;
	}

	public String getUserName() {
		return m_userName;
	}

	public void setUserName(final String a_userName) {
		m_userName = a_userName;
	}

	public String getPassword() {
		return m_password;
	}

	public void setPassword(final String a_password) {
		m_password = a_password;
	}

	public DBCollection getCollection(final String a_name)
			throws MongoException {
		if (getDb() == null) {
			final Mongo mongo = new Mongo(m_servers, m_options);
			setDb(mongo.getDB(m_dbName));
		}

		if (!getDb().isAuthenticated()) {
			if (!getDb().authenticate(m_userName, m_password.toCharArray())) {
				//create default user and pwd
				getDb().addUser(m_userName, m_password.toCharArray());
				if (!getDb().authenticate(m_userName, m_password.toCharArray())) 
					throw new MongoException("Invalid username and password");
			}
		}

		// According to MongoDB doc, you will always get the same instance,
		// there is no need to syncrhonize of cache the collection
		return getDb().getCollection(a_name);
	}

	public void ensureIndexes(final String a_collectionName,
			final String[] a_fields) {
		final BasicDBObject indexes = new BasicDBObject();
		for (String f : a_fields)
			indexes.put(f, 1);
		getCollection(a_collectionName).ensureIndex(indexes);
	}

	public void close() {
		getDb().getMongo().close();
		setDb(null);
	}

	public DB getDb() {
		if(m_db == null){
			final Mongo mongo = new Mongo(m_servers, m_options);
			setDb(mongo.getDB(m_dbName));
		}			
		return m_db;
	}

	public void setDb(DB m_db) {
		this.m_db = m_db;
	}
}
